import type { ComputedRef, Ref } from 'vue';

export interface UsePageFetchParams {
  pageSize: number;
  currentPage: number;
}

export type UsePageFetchCallBack<T extends any[]> = (
  params: UsePageFetchParams,
) => Promise<{ records: T; total: number }> | { records: T; total: number };

export interface UsePageOptions<T extends any[]> {
  /**
   * 请求构造函数
   */
  fetch: UsePageFetchCallBack<T>;

  /**
   * 每页数量 默认:10
   */
  initPageSize?: number;

  /**
   * 初始化当前页， 默认:1
   */
  initCurrentPage?: number;

  /**
   * records使用ref 还是 shallowRef  默认为true  即shallowRef
   */
  shallow?: boolean;

  /**
   * 初始化后是否立即请求 默认true
   */
  immediate?: boolean;

  /**
   * 执行前清空数组 默认false
   */
  resetOnExecute?: boolean;
}

/**
 * 分页请求返回参数
 */
export interface UsePageReturn<T extends any[]> {
  /**
   * 分页数据
   */
  records: Ref<T>;

  /**
   * 当前页
   */
  currentPage: Ref<number>;

  /**
   * 每页数量
   */
  pageSize: Ref<number>;

  /**
   * 接口返回的总数量
   */
  total: Readonly<Ref<number>>;

  /**
   * 总页数
   */
  totalPage: Readonly<Ref<number>>;
  /**
   * 当前是否正在加载中
   */
  isLoading: Readonly<Ref<boolean>>;
  /**
   * 执行请求，可传入分页参数强制执行当前页或每页数量的改变，不传则单纯执行刷新
   */
  execute: (params?: Partial<UsePageFetchParams>) => Promise<void>;
  /**
   * 当前页的数组第一项索引
   */
  startNo: ComputedRef<number>;
}

/**
 * 分页请求
 * @param options
 * @returns
 */
export function usePage<T extends any[]>(options: UsePageOptions<T>): UsePageReturn<T> {
  const refFn = options?.shallow ?? true ? shallowRef : ref;

  const records = refFn([] as any) as Ref<T>;

  const currentPage = ref(options.initCurrentPage ?? 1);

  const pageSize = ref(options.initPageSize ?? 10);

  const total = ref(0);

  const isLoading = ref(false);

  const totalPage = computed(() => {
    return Math.ceil(total.value / pageSize.value);
  });

  const startNo = computed(() => (currentPage.value - 1) * pageSize.value + 1);

  const _fetch = async () => {
    isLoading.value = true;

    options?.resetOnExecute && (records.value.length = 0);

    try {
      const res = await options.fetch({
        currentPage: currentPage.value,
        pageSize: pageSize.value,
      });
      records.value = res.records;
      total.value = Number(res.total) || 0;
    } finally {
      isLoading.value = false;
    }
  };

  watch([currentPage, pageSize], _fetch);

  const execute = async (params?: Partial<UsePageFetchParams>) => {
    if (currentPage.value === 1) {
      await _fetch();
      return;
    }
    if (!!params?.pageSize || !!params?.currentPage) {
      if (pageSize.value !== params?.pageSize || currentPage.value !== params?.currentPage) {
        pageSize.value = Number(params?.pageSize) || pageSize.value;
        currentPage.value = Number(params?.currentPage) || currentPage.value;
      } else {
        await _fetch();
      }
    } else {
      await _fetch();
    }
  };

  const immediate = options?.immediate ?? true;

  immediate && execute();

  return {
    records,
    currentPage,
    pageSize,
    total: shallowReadonly(total),
    isLoading: shallowReadonly(isLoading),
    execute,
    startNo,
    totalPage,
  };
}
